// ? 操作符会自动使用 From::from 将底层错误类型转换为我们的自定义错误类型。

// 在进行错误处理时，为自己的自定义错误类型实现 From 通常很有用。
// 通过将底层错误类型转换为我们自己的自定义错误类型，我们可以在不丢失底层原因信息的情况下返回单一的错误类型。\
use std::fs;
use std::io;
use std::num;

enum CliError {
    IoError(io::Error),
    ParseError(num::ParseIntError),
}

impl From<io::Error> for CliError {
    fn from(error: io::Error) -> Self {
        CliError::IoError(error)
    }
}

impl From<num::ParseIntError> for CliError {
    fn from(error: num::ParseIntError) -> Self {
        CliError::ParseError(error)
    }
}

fn open_and_parse_file(file_name: &str) -> Result<i32, CliError> {
    let mut contents = fs::read_to_string(&file_name)?;
    let num: i32 = contents.trim().parse()?;
    Ok(num)
}

// 相比之下，这个只说明了可以转换，但是转换起来还是太麻烦，需要通用函数来处理。不然有好多重复的代码。



// 正确的做法应该是：   
// 1. 为自定义错误类型实现 From trait，这样 ? 操作符就可以自动将底层错误类型转换为我们的自定义错误类型。
// 2. 为自定义错误类型实现 Display 和 Debug trait，这样我们可以使用 {} 和 {:?} 来打印出错误信息。
// 3. 通用函数来处理错误，这样可以将重复的代码减少，提高代码的可读性。
// 例如：
